﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Security.Cryptography;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Xml.Linq;
using Skyper.Web;
using Skyper.Core;
namespace Skyper.Model {
	public sealed class SkypeSync {
		#region Fields
		private SkypeClient client;

		private const String FileContactsData = "/Contacts.xml";
		private const String FileContactsHash = "/Contacts.hash";
		private Action<ContactModel[]> syncedContacts;
		private Action<Dictionary<String, Stream>> syncedAvatars;
		#endregion
		#region Constructor
		public SkypeSync() {
			
		}
		#endregion
		#region Private
		private void client_CloseCompleted(object sender, AsyncCompletedEventArgs e) {
			if (e.UserState != null) {
				if (e.UserState is Action) {
					Action act = e.UserState as Action;

					if (act != null) {
						act();
					}
				}
			}
		}
		private void client_OpenCompleted(object sender, AsyncCompletedEventArgs e) {
			if (e.Cancelled) {
				return;
			}
			if (e.Error != null) {
				return;
			}
			this.client.GetContactsAsync();
		}
		private void client_GetContactsCompleted(object sender, GetContactsCompletedEventArgs e) {
			if (e.Cancelled || e.Error != null) {
				return;
			}
			String xml = Decompress(e.Result);
			if (String.IsNullOrEmpty(xml)) {
				return;
			}
			var contacts = GetContacts(xml).ToArray();
			this.OnSyncedContacts(contacts);


			this.client.GetRecentAsync();
			this.client.GetAvatarsAsync();
		}
		private void client_GetGroupsCompleted(object sender, GetGroupsCompletedEventArgs e) {

		}
		private void client_GetAvatarsCompleted(object sender, GetAvatarsCompletedEventArgs e) {
			if (e.Cancelled || e.Error != null) {
				return;
			}
			try {
				ThreadPool.QueueUserWorkItem(LoadAvatars, e.Result);
			} catch (Exception exc) {
				if (System.Diagnostics.Debugger.IsAttached) {
					System.Diagnostics.Debugger.Break();
				}
			}
		}
		private void client_GetMessagesCompleted(object sender, GetMessagesCompletedEventArgs e) {
			
		}
		private void client_GetRecentCompleted(object sender, GetRecentCompletedEventArgs e) {
			if (e.Cancelled || e.Error != null) {
				return;
			}
			String xml = Decompress(e.Result);

			if (String.IsNullOrEmpty(xml)) {
				return;
			}

			var chats = GetRecentChats(xml);
		}
		private String Decompress(Byte[] data) {
			if (data == null || data.Length == 0) {
				return String.Empty;
			}
			return Encoding.UTF8.GetString(data, 0, data.Length);
		}

		private void OnSyncedContacts(ContactModel[] array) {
			if (this.syncedContacts != null) {
				this.syncedContacts(array);
			}
		}

		private IEnumerable<ContactModel> GetContacts(String xml) {
			XDocument doc = XDocument.Parse(xml);

			var list = doc.Descendants("Contact");
			foreach (var node in list) {
				ContactModel model = new ContactModel();
				model.Username = node.GetAttributeValue("Username");
				model.City = node.GetAttributeValue("City");
				model.Country = node.GetAttributeValue("Country");
				model.DisplayName = node.GetAttributeValue("Displayname");
				model.Fullname = node.GetAttributeValue("Fullname");
				model.Homepage = node.GetAttributeValue("Homepage");
				model.Mood = node.GetAttributeValue("Mood");
				model.PhoneHome = node.GetAttributeValue("PhoneHome");
				model.PhoneWork = node.GetAttributeValue("PhoneOffice");
				model.PhoneMobile = node.GetAttributeValue("PhoneMobile");
				model.Status = (UserStatus)Int32.Parse(node.GetAttributeValue("Status"));
				yield return model;
			}
		}
		private IEnumerable<ChatModel> GetRecentChats(String xml) {
			XDocument doc = XDocument.Parse(xml);
			var list = doc.Descendants("Chat");
			foreach (var node in list) {
				String created = node.GetAttributeValue("Created");
				String updated = node.GetAttributeValue("Updated");
				String partner = node.GetAttributeValue("Partner");

				var contact = App.Skype.GetContact(partner);
				if (contact == null) {
					continue;
				}


			}
			return null;
		}


		private void LoadAsync() {
			//this.LoadContacts();
		}
		private void LoadContacts() {
			try {
				Byte[] data = null;
				Byte[] hash = null;
				using (var storage = IsolatedStorageFile.GetUserStoreForApplication()) {

					if (storage.FileExists(FileContactsData) && storage.FileExists(FileContactsHash)) {
						Byte[] buffer = new Byte[4096];
						//Read hash
						using (var fs = new IsolatedStorageFileStream(FileContactsHash, FileMode.Open, storage)) {
							using (var mem = new MemoryStream()) {
								int r = 0;
								while ((r = fs.Read(buffer, 0, buffer.Length)) > 0) {
									mem.Write(buffer, 0, r);
								}
								hash = mem.ToArray();
							}
						}
						//Read Data
						using (var fs = new IsolatedStorageFileStream(FileContactsData, FileMode.Open, storage)) {
							using (var mem = new MemoryStream()) {
								int r = 0;
								while ((r = fs.Read(buffer, 0, buffer.Length)) > 0) {
									mem.Write(buffer, 0, r);
								}
								data = mem.ToArray();
							}
						}

						//Validate
						using (var checksum = GetHasher()) {
							Byte[] stuff = checksum.ComputeHash(data);

							if (stuff.Length == hash.Length) {
								Boolean ok = true;
								for (int i = 0; i < stuff.Length; i++) {
									if (stuff[i] != hash[i]) {
										ok = false;
										break;
									}
								}
								if (ok) {
									String value = Encoding.UTF8.GetString(data, 0, data.Length);

									this.LoadContacts(value);
								}
							}
						}
					}
				}
			} 
#if DEBUG
			catch (Exception exc) {
				if (System.Diagnostics.Debugger.IsAttached) {
					System.Diagnostics.Debugger.Break();
				}
				Debug.WriteLine(exc);
			}
#else
			catch {}
#endif
		}
		private void LoadContacts(String xml) {
			try {
				XDocument doc = XDocument.Parse(xml);

				var list = doc.Descendants("Contact");
				var users = new List<ContactModel>();
				foreach (var node in list) {
					ContactModel mod = new ContactModel();
					if (mod.Resume(node)) {
						users.Add(mod);
					}

				}
				this.LoadContacts(users);
			} 
#if DEBUG
			catch (Exception exc) {
				if (System.Diagnostics.Debugger.IsAttached) {
					System.Diagnostics.Debugger.Break();
				}
				Debug.WriteLine(exc);
			}
#else
			catch { }
#endif
		}
		private void LoadContacts(List<ContactModel> list) {
			try {
				using (var iso = IsolatedStorageFile.GetUserStoreForApplication()) {
					if (iso.DirectoryExists("/Avatars")) {
						foreach (var mod in list) {
							String path = "/Avatars/" + mod.Username + ".jpg";

							if (iso.FileExists(path)) {
								using (var fs = iso.OpenFile(path, FileMode.Open, FileAccess.Read, FileShare.Read)) {
									Byte[] data = new Byte[fs.Length];

									if (fs.Read(data, 0, data.Length) == data.Length) {

										mod.SetAvatar(data);
									}
								}
							}
						}
					}
				}
			}
#if DEBUG
 catch (Exception exc) {
				if (System.Diagnostics.Debugger.IsAttached) {
					System.Diagnostics.Debugger.Break();
				}
				Debug.WriteLine(exc);
			}
#else
			catch { }
#endif
			if (this.syncedContacts != null) {
				this.syncedContacts(list.ToArray());
			}
		}
		private void LoadAvatars(Object state) {
			if (state != null && state is Byte[]) {
				this.LoadAvatars((Byte[])state);
			}
		}
		private void LoadAvatars(Byte[] data) {
			Byte[] real = Zipper.Unzip(data);

			String xml = Encoding.UTF8.GetString(real, 0, real.Length);

			this.LoadAvatars(xml);
		}
		private void LoadAvatars(String xml) {
			XDocument doc = XDocument.Parse(xml);
			Dictionary<String, Stream> avatars = new Dictionary<string, Stream>();

			try {
				using (var iso = IsolatedStorageFile.GetUserStoreForApplication()) {
					if (!iso.DirectoryExists("/Avatars")) {
						iso.CreateDirectory("/Avatars");

						foreach (var node in doc.Descendants("Avatar")) {
							String username = node.GetAttributeValue("Username");
							String base64 = node.Value;
							Byte[] image = Convert.FromBase64String(base64);

							using (var fs = iso.OpenFile(String.Format("/Avatars/{0}.jpg", username), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read)) {
								fs.Write(image, 0, image.Length);
								avatars[username] = new MemoryStream(image);
							}
						}
					}

				}
			}
#if DEBUG
 catch (Exception exc) {
				if (System.Diagnostics.Debugger.IsAttached) {
					System.Diagnostics.Debugger.Break();
				}
				Debug.WriteLine(exc);
			}
#else
			catch { }
#endif
			if (this.syncedAvatars != null) {
				this.syncedAvatars(avatars);
			}
		}
		private void LoadGroups() {

		}
		private void LoadMessages() {

		}

		private void SaveAsync() {
			this.SaveContacts();
		}
		private void SaveContacts() {
			//Write to memories
			Byte[] data = null;
			XElement root = new XElement("Contacts");
			foreach (var c in App.Skype.Contacts) {
				var child = c.Suspend();
				if (child != null) {
					root.Add(child);
				}
			}
			data = Encoding.UTF8.GetBytes(root.ToString());

			try {
				using (var checksum = this.GetHasher()) {
					Byte[] hash = checksum.ComputeHash(data);
					using (var storage = IsolatedStorageFile.GetUserStoreForApplication()) {

						using (var fs = new IsolatedStorageFileStream(FileContactsHash, FileMode.Create, storage)) {
							fs.Write(hash, 0, hash.Length);
						}
						using (var fs = new IsolatedStorageFileStream(FileContactsData, FileMode.Create, storage)) {
							fs.Write(data, 0, data.Length);
						}
					}

				}
			}
#if DEBUG
 catch (Exception exc) {
				if (System.Diagnostics.Debugger.IsAttached) {
					System.Diagnostics.Debugger.Break();
				}
				Debug.WriteLine(exc);
			}
#else
			catch {}
#endif
		}
		private HashAlgorithm GetHasher() {
			return new SHA1Managed();
		}
		#endregion
		#region Public
		public void Start() {

			if (this.client != null && (this.client.State == CommunicationState.Opening || this.client.State == CommunicationState.Opened)) {
				this.Stop();
			}
			this.client = new SkypeClient(new BasicHttpBinding() {
				MaxReceivedMessageSize = 1024 * 1024,
			}, new EndpointAddress(String.Format("{0}Skype",App.Skype.Config.Hostname)));
			this.client.OpenCompleted += (client_OpenCompleted);
			this.client.CloseCompleted += (client_CloseCompleted);
			this.client.GetContactsCompleted += (client_GetContactsCompleted);
			this.client.GetGroupsCompleted += (client_GetGroupsCompleted);
			this.client.GetAvatarsCompleted += (client_GetAvatarsCompleted);
			this.client.GetMessagesCompleted += new EventHandler<GetMessagesCompletedEventArgs>(client_GetMessagesCompleted);
			this.client.GetRecentCompleted += new EventHandler<GetRecentCompletedEventArgs>(client_GetRecentCompleted);
			this.client.OpenAsync();
		}
		public void Stop() {
			this.client.OpenCompleted -= (client_OpenCompleted);
			this.client.CloseCompleted -= (client_CloseCompleted);
			this.client.GetContactsCompleted -= (client_GetContactsCompleted);
			this.client.GetGroupsCompleted -= (client_GetGroupsCompleted);
			this.client.GetAvatarsCompleted -= (client_GetAvatarsCompleted);
			this.client.CloseAsync();
		}
		public void Load() {
			if (App.IsDesignTime()) {
				return;
			}
			Thread thread = new Thread(LoadAsync);
			thread.Name = "Loading Thread";
			thread.IsBackground = true;
			thread.Start();
		}
		public void Save() {
			if (App.IsDesignTime()) {
				return;
			}
			SaveAsync();
		}

		public void SyncMessages(String username) {
			this.client.GetMessagesAsync(username, DateTime.MinValue);
		}
		#endregion
		#region Events
		public event Action<ContactModel[]> SyncedContacts {
			add {
				syncedContacts = Action<ContactModel[]>.Combine(syncedContacts, value) as Action<ContactModel[]>;
			}
			remove {
				syncedContacts = Action<ContactModel[]>.Remove(syncedContacts, value) as Action<ContactModel[]>;
			}
		}
		public event Action<Dictionary<String, Stream>> SyncedAvatars {
			add {
				syncedAvatars = Action<Dictionary<String, Stream>>.Combine(syncedAvatars, value) as Action<Dictionary<String, Stream>>;
			}
			remove {
				syncedAvatars = Action<Dictionary<String, Stream>>.Remove(syncedAvatars, value) as Action<Dictionary<String, Stream>>;
			}
		}
		public event Action<ContactModel, List<MessageModel>> SyncedMessages;
		#endregion
	}
}
